using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace it.mintlab.mobilenet.mintar
{
    public abstract class Model
    {
        /// <summary>
        /// Clone model object 
        /// </summary>
        /// <returns></returns>
        public Model clone()
        {
            Model modelCopy = (Model)this.GetType().GetConstructor(new Type[] { }).Invoke(null);
            
            PropertyInfo[] properties = this.GetType().GetProperties();
            foreach (PropertyInfo pi in properties)
            {
                //if any attribute is present
                ModelAttr[] attrs = (ModelAttr[])pi.GetCustomAttributes(typeof(ModelAttr), true);
                if (attrs.Length == 1)
                {
                    object obj = pi.GetValue(this, null);

                    if (obj.GetType().Equals(typeof(DateTime)))
                    {
                        DateTime dt = new DateTime(((DateTime)obj).Ticks);
                        pi.SetValue(modelCopy, dt, null);
                    }
                    // string, int, decimal, bool
                    else 
                    {
                        pi.SetValue(modelCopy, obj, null);
                    }
                }
            }

            return modelCopy;
        }

        /// <summary>
        /// Ovverided Equals Method
        /// </summary>
        /// <param name="obj"></param>
        /// <returns>Return true if key is the same</returns>
        public override bool Equals(object obj)
        {
            if (obj is Model)
            {
                PropertyInfo[] properties = this.GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
                foreach (PropertyInfo pi in properties)
                {
                    ModelAttr[] attrs = (ModelAttr[])pi.GetCustomAttributes(typeof(ModelAttr), true);
                    if (attrs.Length == 1)
                    {
                        if (((ModelAttr)attrs[0]).key)
                        {
                            if (!pi.GetValue(this, null).Equals(obj.GetType().GetProperty(pi.Name).GetValue(obj, null)))
                                return false;
                        }
                    }
                }
                return true;
            }
            else
            {
                return base.Equals(obj);
            }
        }

        /// <summary>
        /// Ovverided GetHashCode Method
        /// </summary>
        /// <param name="obj"></param>
        /// <returns>Return true if key is the same</returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Create Model from
        /// </summary>
        /// <param name="modelType"></param>
        /// <returns></returns>
        public static Model createModel(Type modelType)
        {
            ConstructorInfo constructor = modelType.GetConstructor(new Type[0]);
            return (Model)constructor.Invoke(null);
        }

        /// <summary>
        /// Insert model in DB without model changes
        /// </summary>
        /// <returns>true if insertion done</returns>
        public abstract bool insert();

        /// <summary>
        /// Insert model in DB and change his identity value
        /// </summary>
        public abstract void insertIdentity();

        /// <summary>
        /// Update model in DB by Keys
        /// </summary>
        /// <returns></returns>
        public abstract bool updateByKey();

        /// <summary>
        /// Delete model in DB by Keys
        /// </summary>
        /// <returns></returns>
        public abstract bool deleteByKey();

        /// <summary>
        /// Get Model by key columns
        /// </summary>
        /// <returns>true id found</returns>
        public abstract bool selectByKey();

        /// <summary>
        /// Get Model by key columns specifying annotation class (for ISA)
        /// </summary>
        /// <param name="modelType">annotation class</param>
        /// <returns>Model selected</returns>
        public abstract bool selectByKey(Type modelType);

        /// <summary>
        /// Get List of all records
        /// </summary>
        /// <returns>records</returns>
        public abstract Model selectFirst();

        /// <summary>
        /// Get first record
        /// </summary>
        /// <param name="modelType">annotation class</param>
        /// <returns>records</returns>
        public abstract Model selectFirst(Type modelType);

        /// <summary>
        /// Get first record by sql filter 
        /// </summary>
        /// <returns>true id found</returns>
        public abstract Model selectFirstByFilter(String filter);

        /// <summary>
        /// Get first record by sql filter 
        /// </summary>
        /// <param name="modelType">annotation class</param>
        /// <returns>Model selected</returns>
        public abstract Model selectFirstByFilter(Type modelType, String filter);

        /// <summary>
        /// Get List of all records
        /// </summary>
        /// <returns>records</returns>
        public abstract List<Model> select();

        /// <summary>
        /// Get List of all records
        /// </summary>
        /// <param name="modelType">annotation class</param>
        /// <returns>records</returns>
        public abstract List<Model> select(Type modelType);

        /// <summary>
        /// Get List of records by sql filter 
        /// </summary>
        /// <returns>true id found</returns>
        public abstract List<Model> selectByFilter(String filter);

        /// <summary>
        /// Get List of records by sql filter 
        /// </summary>
        /// <param name="modelType">annotation class</param>
        /// <returns>Model selected</returns>
        public abstract List<Model> selectByFilter(Type modelType, String filter);
    }
}
